import {lobby} from "db://assets/script/lobby";
import {GameEvent} from "db://assets/script/game-event";
import {userInfo} from "db://assets/script/user-info";
import {_decorator, Button, Component, director, EditBox, Label, Node} from "cc";
import {http} from "db://assets/script/common/HTTP";
import {ConfigsResult, NowResult} from "db://assets/proto/new-world-api/v1/system";
import {serverTimeHelper} from "db://assets/script/server-time-helper";
import {configsHelper} from "db://assets/script/configs-helper";
import {AssetRequest, AssetResult, BaseInfoRequest, BaseInfoResult} from "db://assets/proto/new-world-api/v1/user";
import {AssetType} from "db://assets/proto/shared/v1/props";
import {AssetCoreChange} from "db://assets/script/common/events/asset-events/asset-core-change";
import {cmm} from "db://assets/script/utils/cmm";
import {BuildingJoinRequest, BuildingJoinResult} from "db://assets/proto/new-world-api/v1/building";
import {MixPipesRequest, MixPipesResult} from "db://assets/proto/new-world-api/v1/mix";
import {FishListResult} from "db://assets/proto/new-world-api/v1/fish";
import {
    AuthRequest,
    AuthResult,
    SimpleRegisterRequest,
    SimpleRegisterResult
} from "db://assets/proto/new-world-api/v1/auth";
import {socket} from "db://assets/script/common/socket";
import CryptoJS from "crypto-js";

const {ccclass, property} = _decorator;

@ccclass('LoginPage')
export class LoginPage extends Component {

    @property(Node)
    loginPlane!: Node;
    @property(Node)
    registerPlane!: Node;

    @property(EditBox)
    loginAccountEdit!: EditBox

    @property(EditBox)
    loginPasswordEdit!: EditBox


    @property(EditBox)
    registerAccountEdit!: EditBox

    @property(EditBox)
    registerPasswordEdit!: EditBox

    @property(EditBox)
    registerPasswordRepeatEdit!: EditBox

    @property(Button)
    loginBtn!: Button

    @property(Button)
    registerBtn!: Button

    @property(Button)
    goLoginBtn!: Button

    @property(Button)
    goRegisterBtn!: Button

    @property(Label)
    loginWaiting!: Label

    status: number = 1
    LOGIN = 2;
    REGISTER = 3;

    start() {
        this.loginBtn.node.on(Button.EventType.CLICK, this._login, this)
        this.registerBtn.node.on(Button.EventType.CLICK, this._register, this)
        this.goLoginBtn.node.on(Button.EventType.CLICK, () => this.changeStatus(this.LOGIN), this)
        this.goRegisterBtn.node.on(Button.EventType.CLICK, () => this.changeStatus(this.REGISTER), this)
        const uid = localStorage.getItem("uid")
        const accessToken = localStorage.getItem("world-access-token")
        userInfo.assetInfo.assets = {}
        userInfo.cityInfo.inEdit = false
        if (uid && accessToken) {
            this.connect2Game(parseInt(uid), accessToken)
        } else {
            this.changeStatus(this.LOGIN)
        }
    }

    connect2Game(uid: number, accessToken: string) {
        this.loginWaiting.node.active = true
        this.registerPlane.active = false
        this.loginPlane.active = false
        http.setUid(uid)
        http.setToken(accessToken)
        userInfo.setUid(uid.toString())
        socket.closeConnect()
        socket.connect(uid.toString(), accessToken, () => {
                Promise.all([this._joinCity(), this._loadFishes(), this._cityResourceProduceAt()])
                    .then(_ => this._initSystemConfigs())
                    .then(_ => this._initServerTime())
                    .then(_ => this._initBaseInfo())
                    .then(_ => this._initAssets())
                    .then(_ => lobby.instance.change2Main())
                    .catch(error => {
                        console.log("请求失败", error)
                        this.changeStatus(this.LOGIN)
                    })
            }
        )
    }


    async _register(): Promise<void | SimpleRegisterResult> {
        if (this.status != this.REGISTER) return
        const face = this.registerAccountEdit.string
        const fKey = this.registerPasswordEdit.string
        const fKeyRepeat = this.registerPasswordRepeatEdit.string
        if (!face) {
            lobby.instance.showToast('用户名不可为空')
            return
        }
        if (!fKey) {
            lobby.instance.showToast('密码不可为空')
            return
        }
        if (fKey != fKeyRepeat) {
            lobby.instance.showToast('两次输入密码不一致')
            return
        }
        const param = SimpleRegisterRequest.create({
            account: face,
            password: CryptoJS.MD5(fKey).toString(),
        })!
        return http.requestAsync<SimpleRegisterResult>("POST", "v0/simple/register", SimpleRegisterResult, SimpleRegisterRequest.encode(param).finish())
            .then(res => {
                if (!res) return;
                console.log('uid: ', res.uid.toNumber())
                localStorage.setItem('uid', res.uid.toString())
                localStorage.setItem('world-access-token', res.accessToken)
                userInfo.setUid(res.uid.toString());
                return res;
            })
            .then(res => this.connect2Game(res!.uid.toNumber(), res!.accessToken))
            .catch(error => console.log("注册失败", error))
    }

    async _login(): Promise<void | AuthResult> {
        if (this.status != this.LOGIN) return
        const face = this.loginAccountEdit.string
        const fKey = this.loginPasswordEdit.string
        if (!face) {
            lobby.instance.showToast('用户名不可为空')
            return
        }
        if (!fKey) {
            lobby.instance.showToast('密码不可为空')
            return
        }
        const param = AuthRequest.create({
            account: face,
            password: CryptoJS.MD5(fKey).toString(),
            authType: 0
        })!
        return http.requestAsync<AuthResult>("POST", "v0/auth", AuthResult, AuthRequest.encode(param).finish())
            .then(res => {
                if (!res) return;
                console.log('uid: ', res.uid.toNumber())
                localStorage.setItem('uid', res.uid.toString())
                localStorage.setItem('world-access-token', res.accessToken)
                userInfo.setUid(res.uid.toString());
                return res;
            })
            .then(res => this.connect2Game(res!.uid.toNumber(), res!.accessToken))
            .catch(error => {
                if (error.code == 401) {
                    lobby.instance.showToast('账号或密码错误')
                }
                console.log("登录失败", error)
            })
    }


    async _initServerTime(): Promise<void> {
        return http.requestAsync("POST", "v1/system/now", NowResult)
            .catch(error => console.log("获取服务器时间失败", error))
            .then(nowMs => serverTimeHelper.setServerTime(nowMs?.nowMs.toNumber()))
    }

    async _initSystemConfigs(): Promise<void> {
        return http.requestAsync("POST", "v1/system/configs", ConfigsResult)
            .catch(error => console.log("获取配置失败", error))
            .then(configs => configsHelper.enrichCore(configs!))
    }

    async _initAssets(): Promise<void> {
        const param = AssetRequest.create({
            assetTypes: [AssetType.asset_type_basic, AssetType.asset_type_city_building],
        })
        return http.requestAsync("POST", "v1/user/asset", AssetResult, AssetRequest.encode(param).finish())
            .catch(error => console.log("获取资产失败", error))
            .then(resp => director.emit(GameEvent.assetCoreChange, new AssetCoreChange(cmm.map2kv(resp!.assets, (k1, v1) => {
                return {
                    k2: k1.toNumber(),
                    v2: v1,
                }
            }))))
    }

    async _initBaseInfo(): Promise<void> {
        const param = BaseInfoRequest.create({})
        return http.requestAsync("POST", "v1/user/info", BaseInfoResult, BaseInfoRequest.encode(param).finish())
            .catch(error => console.log("获取基础信息失败", error))
            .then(resp => {
                userInfo.setBaseInfo(resp!.username, resp!.avatar)
                lobby.instance.showToast('欢迎回来，' + resp!.username + '！')
            })
    }

    changeStatus(status: number) {
        console.log('try change', status)
        switch (status) {
            case this.LOGIN:
                this.change2Login()
                break;
            case this.REGISTER:
                this.change2Register()
                break;
            default:
                this.change2Login()
        }
    }


    async _joinCity(): Promise<void> {
        const param = BuildingJoinRequest.create({mapId: 1})
        return http.requestAsync<BuildingJoinResult>("POST", "v1/building/join", BuildingJoinResult, BuildingJoinRequest.encode(param).finish())
            .then(buildings => userInfo.cityInfo.setBuildings(1, buildings.buildings, buildings.unlockCellIds))
            .catch(() => console.log("进入地图失败"))
    }

    async _cityResourceProduceAt(): Promise<void> {
        const param = MixPipesRequest.create({})
        return http.requestAsync<MixPipesResult>("POST", "v1/mixing/pipes", MixPipesResult, MixPipesRequest.encode(param).finish())
            .then(resp => userInfo.mixInfo.setMixInfos(resp.infos))
            .catch(() => console.log("进入地图失败"))
    }

    async _loadFishes(): Promise<void> {
        return http.requestAsync<FishListResult>("POST", "v1/fish/list", FishListResult)
            .then(fishes => userInfo.fishInfo.setFishes(fishes.list))
            .catch(() => console.log("获取鱼列表失败"))
    }

    change2Login() {
        this.status = this.LOGIN
        this.registerPlane.active = false
        this.loginPlane.active = true
    }

    change2Register() {
        this.status = this.REGISTER
        this.registerPlane.active = true
        this.loginPlane.active = false
    }

}